#ifndef __SUBSYSTEM_FLUID_BLOCK_BEHAVIOR_H__
#define __SUBSYSTEM_FLUID_BLOCK_BEHAVIOR_H__

#include <map>
#include <set>
#include <memory>
#include "SubsystemBlockBehavior.hpp"
#include "Engine/Point3.hpp"


using namespace PocketSurvival::Engine;

namespace PocketSurvival
{
    class FluidBlock;
    class SubsystemTerrain;

    enum class FluidType
    {
        Water,
        Magma
    };
    
    class SubsystemFluidBlockBehavior : public SubsystemBlockBehavior
    {
    protected:
        FluidType m_fluidType;
        FluidBlock *m_fluidBlock = nullptr;
        std::set<Point3> m_toUpdate;
        std::map<Point3, int32_t> m_toSet;
        std::map<Point3, int32_t> m_visited;
        std::map<Point3, Vector2> m_fluidRandomFlowDirections;
        bool m_generateSources;
        SubsystemTerrain *subsystemTerrain = nullptr;

    protected:
        bool isTheSameFluid(int32_t contents);

    public:
        SubsystemFluidBlockBehavior(bool generateSources);
        
        void updateIsTop(int32_t value, int32_t x, int32_t y, int32_t z);
        virtual void onBlockAdded(int32_t value, int32_t oldValue, int32_t x, int32_t y, int32_t z) override;
		virtual void onBlockModified(int32_t value, int32_t oldValue, int32_t x, int32_t y, int32_t z) override;
        virtual void onNeighborBlockChanged(int32_t x, int32_t y, int32_t z, int32_t neighborX, int32_t neighborY, int32_t neighborZ) override;
        virtual void onItemHarvested(int32_t x, int32_t y, int32_t z, int32_t blockValue, BlockDropValue &outDropValue, int32_t &outNewBlockValue) override;
        std::shared_ptr<float> getSurfaceHeight(int32_t x, int32_t y, int32_t z, FluidBlock* &surfaceFluidBlock);
        std::shared_ptr<float> getSurfaceHeight(int32_t x, int32_t y, int32_t z);
        std::shared_ptr<Vector2> calculateFlowSpeed(int32_t x, int32_t y, int32_t z, FluidBlock* &surfaceBlock, std::shared_ptr<float> &outSurfaceHeight);
        std::shared_ptr<Vector2> calculateFlowSpeed(int32_t x, int32_t y, int32_t z);
        void spreadFluid();
        virtual bool onFluidInteract(int32_t interactValue, int32_t x, int32_t y, int32_t z, int32_t fluidValue);
        void set(int32_t x, int32_t y, int32_t z, int32_t value);
        void flowTo(int32_t x, int32_t y, int32_t z, int32_t level);

        virtual void load(const nlohmann::json& json) override;

    private:
        int32_t levelAtNearestFall(int32_t x, int32_t y, int32_t z, int32_t level, std::map<Point3, int32_t> &levels);

    };

    inline SubsystemFluidBlockBehavior::SubsystemFluidBlockBehavior(bool generateSources) : m_generateSources(generateSources) {}
}

#endif